﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.ComponentModel;

namespace Expand
{
    public static class ExEnvironment
    {
        public static class Windows
        {
            #region Windows API
            [DllImport("user32.dll")]
            private static extern IntPtr GetForegroundWindow();

            [DllImport("user32.dll")]
            [return : MarshalAs(UnmanagedType.Bool)]
            private static extern bool SetForegroundWindow([In] IntPtr hWnd);
            #endregion

            private static BackgroundWorker _Worker = new BackgroundWorker();
            private static volatile bool _ApplicationClosing = false;
            private static volatile int _ListenersPresent = 0;
            private static volatile IntPtr _ForegroundWindow = IntPtr.Zero;
            public static IntPtr ForegroundWindow
            {
                get { return GetForegroundWindow(); }
                set
                {
                    if (ForegroundWindow == value) return;
                    IntPtr last = ForegroundWindow;
                    if (SetForegroundWindow(value))
                    {
                        _ForegroundWindow = value;
                        On_ForegroundWindowChanged(ForegroundWindow, new LastValueEventArgs<IntPtr>(last));
                    }
                    else
                        throw new InvalidOperationException("Window can't be set as foreground");
                }
            }

            static Windows()
            {
                _ForegroundWindow = GetForegroundWindow();
                Application.ApplicationExit += new EventHandler(Application_ApplicationExit);
                _Worker.DoWork += new DoWorkEventHandler(Work);
            }

            private static void Application_ApplicationExit(object sender, EventArgs e)
            {
                _ApplicationClosing = true;
            }

            private static void Work(object sender, DoWorkEventArgs e)
            {
                if (!_ApplicationClosing && _ListenersPresent > 0)
                    On_ForegroundWindowChanged(null, new LastValueEventArgs<IntPtr>(IntPtr.Zero));

                while (!_ApplicationClosing && _ListenersPresent > 0)
                {
                    CheckForegroundWindowChanged();
                    System.Threading.Thread.Sleep(1);
                }
            }

            private static void On_ForegroundWindowChanged(object sender, LastValueEventArgs<IntPtr> e)
            {
                if (_ForegroundWindowChanged != null) _ForegroundWindowChanged(sender, e);
            }

            private static void CheckForegroundWindowChanged()
            {
                IntPtr handle = GetForegroundWindow();
                IntPtr last = _ForegroundWindow;
                bool changed = Environment.Is64BitOperatingSystem ? handle.ToInt64() != last.ToInt64() : handle.ToInt32() != last.ToInt32();

                if (changed)
                {
                    _ForegroundWindow = handle;
                    On_ForegroundWindowChanged(handle, new LastValueEventArgs<IntPtr>(last));
                }
            }

            private static event EventHandler<LastValueEventArgs<IntPtr>> _ForegroundWindowChanged;
            public static event EventHandler<LastValueEventArgs<IntPtr>> ForegroundWindowChanged
            {
                add
                {
                    int tmp = _ListenersPresent;
                    _ForegroundWindowChanged += value;
                    ++_ListenersPresent;
                    if (tmp == 0)
                        _Worker.RunWorkerAsync();
                }
                remove
                {
                    _ForegroundWindowChanged -= value;
                    --_ListenersPresent;
                }
            }
        }
    }
}
